﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SLPropertyGrid.System.ComponentModel;

namespace SLPropertyGrid
{
    public abstract class PropertyGridBase : Control
    {

        public static readonly DependencyProperty InstanceProperty
            = DependencyProperty.Register("Instance", typeof(object), typeof(PropertyGridBase),
                                          new PropertyMetadata(null, OnInstanceChanged)
                );

        public static readonly DependencyProperty ContourColorProperty
    = DependencyProperty.Register("ContourColor", typeof(Color), typeof(PropertyGridBase), new PropertyMetadata(Color.FromArgb(0xFF, 0xE9, 0xEC, 0xFA))
                                  
        );

        private IList<PropertyCategory> _properties;

        protected ContentControl layoutRoot;

        public PropertyGridBase()
        {
            DefaultStyleKey = typeof(PropertyGridBase);

            this.BindingValidationError += Root_BindingValidationError;
        }

        public object Instance
        {
            get
            {
                return base.GetValue(InstanceProperty);
            }
            set
            {
                base.SetValue(InstanceProperty, value);
            }
        }

        public Color ContourColor
        {
            get
            {
                return (Color)GetValue(ContourColorProperty);
            }
            set
            {
                SetValue(ContourColorProperty,value);
            }
        }

        protected IList<PropertyCategory> Properties
        {
            get { return _properties; }
            set { _properties = value; }
        }


        private static void OnInstanceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PropertyGridBase propertyGrid = d as PropertyGridBase;
            if (propertyGrid != null)
            {
                propertyGrid.Properties = ExtractPropertiesWithCategory(e.NewValue);
                propertyGrid.GenerateUI();
                propertyGrid.DataContext = propertyGrid.Instance;
            }
        }

        protected abstract void GenerateUI();
        //{
        //    //GenerateUIWithPanels();
        //    GenerateUIWithGrid();
        //}

        private static PropertyCollection ExtractProperties(object ob)
        {
            PropertyCollection pc = new PropertyCollection();
            Type t = ob.GetType();
            var props = t.GetProperties();

            foreach (PropertyInfo pinfo in props)
            {
                Property prop = new Property(ob, pinfo);
                pc.Add(prop);
                if (prop.Category != null)
                    Debug.WriteLine("Property {0} has cat {1}", prop.DisplayName, prop.Category);
            }

            return pc;
        }

        private static IList<PropertyCategory> ExtractPropertiesWithCategory(object ob)
        {
            Dictionary<string, PropertyCategory> _dict = new Dictionary<string, PropertyCategory>();
            //PropertyCategory pc = new PropertyCollection();
            Type t = ob.GetType();
            var props = t.GetProperties();

            foreach (PropertyInfo pinfo in props)
            {
                ParameterInfo[] parameters = pinfo.GetIndexParameters();
                //we don't support indexed properties at the moment
                if (parameters.Length == 0)
                {
                    Property prop = new Property(ob, pinfo);
                    string cat = prop.Category ?? "Misc";
                    if (!_dict.ContainsKey(cat))
                        _dict.Add(cat, new PropertyCategory() { Name = cat });
                    _dict[cat].Properties.Add(prop);
                }

            }

            return _dict.Values.ToList();
        }

        private void Root_BindingValidationError(object sender, ValidationErrorEventArgs e)
        {



        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            layoutRoot = GetTemplateChild("LayoutRoot") as ContentControl;

            DataContext = Instance;
            GenerateUI();
        }



        //public void ExploreVisualTree()
        //{
        //    IList<DependencyObject> descendants = Helper.ExploreVisualTree(this, o => true);


        //}






        protected FrameworkElement GetControlForProperty(Property property)
        {
            Control c;
            c = GetControlForType(property.PropertyType);
            DependencyProperty prop = GetPropertyFromType(property.PropertyType);
            BindPropertyToControl(property, c, prop);

            DependencyProperty propertyToBind;
            Control editor = property.GetEditor(out propertyToBind) as Control;
            if( (editor != null)&&propertyToBind!=null)
            {
                return WrapCustomEditor(property, c, editor,propertyToBind);
            }
            return c;
        }

        private void BindPropertyToControl(Property property, Control c, DependencyProperty prop)
        {
            Binding binding = new Binding(property.Name);
            binding.Mode = BindingMode.TwoWay;
            binding.ValidatesOnExceptions = true;
            binding.NotifyOnValidationError = true;
            binding.Converter = property.PropertyType.IsEnum ? new DataFormToStringConverter() : null;
            c.SetBinding(prop, binding);
            if(!property.CanWrite)
                c.IsEnabled = false;
        }

        private FrameworkElement WrapCustomEditor(Property property, Control c, Control editor, DependencyProperty propertyToBind)
        {
            ChildWindow chw = new ChildWindow();
            chw.MinHeight = 300;
            chw.MinWidth = 400;
            chw.DataContext = Instance;
            chw.Content = editor;
            BindPropertyToControl(property,editor,propertyToBind);
            Button btn = new Button();
            btn.Content = "...";
            btn.Click += delegate
                             {
                                 chw.DataContext = Instance;
                                 chw.Show();
                             };
            //btn.Click+=((sender,args)=> 
            StackPanel sp = new StackPanel();
            sp.Orientation = Orientation.Horizontal;
            //sp.HorizontalAlignment = HorizontalAlignment.Stretch;
            c.HorizontalAlignment = global::System.Windows.HorizontalAlignment.Stretch;
            sp.Children.Add(c);
            sp.Children.Add(btn);
            return sp;
        }


        protected Control GetControlForType(Type type)
        {
            if (type == typeof(bool))
            {
                return new CheckBox();
            }
            if (type == typeof(bool?))
            {
                CheckBox box = new CheckBox();
                box.IsThreeState = true;
                return box;
            }
            if ((type == typeof(DateTime)) || (type == typeof(DateTime?)))
            {
                return new DatePicker();
            }
            if (!type.IsEnum)
            {
                return new TextBox();
            }
            ComboBox box2 = new ComboBox();
            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);
            List<string> list = new List<string>();
            foreach (FieldInfo info in fields)
            {
                Enum enum2 = info.GetValue(null) as Enum;
                if (enum2 != null)
                {
                    list.Add(enum2.ToString());
                }
            }
            box2.ItemsSource = list;
            return box2;
        }

        protected static DependencyProperty GetPropertyFromType(Type type)
        {
            if ((type == typeof(bool)) || (type == typeof(bool?)))
            {
                return ToggleButton.IsCheckedProperty;
            }
            if ((type == typeof(DateTime)) || (type == typeof(DateTime?)))
            {
                return DatePicker.SelectedDateProperty;
            }
            if (type.IsEnum)
            {
                return Selector.SelectedItemProperty;
            }
            return TextBox.TextProperty;
        }

 

    }
}
